# microbench: CPU instructions benchmark based on google benchmark framework
# Copyright (C) 2022 Wu Zhangjin <falcon@ruma.tech>

# Build and run microbench for target ARCH on target machine

# Usage:
#
# X86_64:
#     $ make
#
# Others:
#     $ cp test/x86_64.cc test/$(ARCH).cc
#     $ // customize the instructions in test/$(ARCH).cc, please refer to $(ARCH) ISA
#     $ make

# src: benchmark/test/$(TEST_BIN).cc
# bin: benchmark/build/test/$(TEST_BIN)

ARCH      ?= $(shell uname -m)

TEST_BIN  := $(ARCH)
TEST_NAME := $(ARCH)

ifneq ($(O),)
  OPTIMIZE_LEVEL := $(O)
else
  OPTIMIZE_LEVEL := 1
endif

BENCHMARK_CMAKELIST := test/CMakeLists.txt

BENCHMARK_GITHUB    := https://github.com/google/benchmark.git
BENCHMARK_GITEE     := https://gitee.com/mirrors/benchmark.git

GOOGLETEST_GITHUB   := https://github.com/google/googletest.git
GOOGLETEST_GITEE    := https://gitee.com/mirrors/googletest.git

# Use gitee mirror by default
BENCHMARK_MIRROR    := $(BENCHMARK_GITEE)
GOOGLETEST_MIRROR   := $(GOOGLETEST_GITEE)

# FIXME: No cross compiling support currently
TOOL_LIST    ?= cmake gcc g++
TOOL_INSTALL ?= $(shell for t in $(TOOL_LIST); do which $${t} >/dev/null 2>&1 || echo $${t} | sed -e "s/\(.*\)-\([^-]*\)/\2-\1/g"; done)
TOOL_FILES   := $(addprefix /usr/bin/,$(TOOL_LIST))

ifeq ($(wildcard test/$(TEST_BIN).cc),)
  $(error Please add support for $(ARCH), please refer to test/x86_64.cc and add new test/$(TEST_BIN).cc, cpumodel and product variables should be customized too.)
endif

ifeq ($(ARCH),x86_64)
  cpumodel   := $$(grep -m1 -i 'model name' /proc/cpuinfo  | tr '\t' ' ' | tr -s ' ' | cut -d ' ' -f4- | tr -c '[a-zA-Z0-9\.]' '-' | tr -s '-' | sed -e 's/-$$//g')
  product    := $$(cat /sys/class/dmi/id/product_name | cut -d ' ' -f1)
endif

ifeq ($(ARCH),riscv64)
  # FIXME: cpu freq not in /proc/cpuinfo, should get it from /sys/devices/system/cpu/cpu0/cpufreq/{base_frequency,scaling_min_freq,scaling_max_freq}
  cpumodel   := $$(egrep -m3 -i 'uarch|isa|mmu' /proc/cpuinfo  | tr '\t' ' ' | tr -s ' ' | cut -d ':' -f2 | sed -e "s/^ //g"| tr -c '[a-zA-Z0-9\.]' '-' | tr -s '-' | sed -e 's/-$$//g')
  product    := $$(dmesg | grep -i 'Machine model' | cut -d ':' -f2 | sed -e "s/^ //g" | tr ' ' -f2)
endif

ifeq ($(cpumodel),)
  $(error Please add cpumodel info for $(ARCH))
endif
ifeq ($(product),)
  $(error Please add product info for $(ARCH))
endif

ifneq ($(wildcard benchmark/test/$(TEST_BIN).cc),)
  ifeq ($(shell grep "#define OPTIMIZE_LEVEL $(OPTIMIZE_LEVEL)" benchmark/test/$(TEST_BIN).cc),)
    dummy := $(shell sed -i -e "s/#define OPTIMIZE_LEVEL.*/#define OPTIMIZE_LEVEL $(OPTIMIZE_LEVEL)/g" benchmark/test/$(TEST_BIN).cc)
  endif
endif

all: benchmark/build/test/$(TEST_BIN)
	benchmark/build/test/$(TEST_BIN)

env: $(TOOL_FILES)

$(TOOL_FILES):
	$(Q)if [ -n "$(TOOL_INSTALL)" ]; then \
	  sudo apt update -y; \
	  sudo apt install -y --no-install-recommends $(TOOL_INSTALL); \
	fi

benchmark/build/test/$(TEST_BIN): benchmark/build/third_party/googletest/src benchmark/test/$(TEST_BIN).dep $(TOOL_FILES)
	cd benchmark && cmake -DCMAKE_BUILD_TYPE=Release -S . -B "build"
	cd benchmark && cmake --build "build" --config Release --target $(TEST_BIN)

benchmark/test/$(TEST_BIN).dep: benchmark/test/$(TEST_BIN).cc
	cd benchmark && \
	git checkout -- $(BENCHMARK_CMAKELIST) && \
	sed -i -e "/compile_benchmark_test(basic_test)/icompile_benchmark_test($(TEST_BIN))" $(BENCHMARK_CMAKELIST) && \
	sed -i -e "/compile_benchmark_test(basic_test)/iadd_test(NAME $(TEST_NAME) COMMAND $(TEST_BIN) --benchmark_min_time=0.01)\n" $(BENCHMARK_CMAKELIST)
	touch $@

benchmark/test/$(TEST_BIN).cc: test/$(TEST_BIN).cc
	cd benchmark/test && cp ../../test/$(TEST_BIN).cc $(TEST_BIN).cc
	grep "#define OPTIMIZE_LEVEL $(OPTIMIZE_LEVEL)" benchmark/test/$(TEST_BIN).cc || \
	sed -i -e "s/#define OPTIMIZE_LEVEL.*/#define OPTIMIZE_LEVEL $(OPTIMIZE_LEVEL)/g" $@

benchmark/src:
	git clone $(BENCHMARK_MIRROR)

benchmark/build/third_party/googletest/src: benchmark/src
	sed -i -e "s%$(GOOGLETEST_GITHUB)%$(GOOGLETEST_MIRROR)%g" benchmark/WORKSPACE
	sed -i -e "s%$(GOOGLETEST_GITHUB)%$(GOOGLETEST_MIRROR)%g" benchmark/cmake/GoogleTest.cmake.in
	cd benchmark && cmake -E make_directory "build"
	cd benchmark && cmake -E chdir "build" cmake -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release ../

logging:
	@tmpfile=$$(mktemp) \
	logfile=logs/$(product)-$(cpumodel)-$(TEST_NAME)-$$(date +"%Y%m%d-%H%M%S")-O$(OPTIMIZE_LEVEL).log \
	&& make --no-print-directory 2>&1 | tee -a $$tmpfile \
	&& mkdir -p logs \
	&& cat $$tmpfile | col -bp > $$logfile && rm $$tmpfile

clean:
	rm -rf benchmark/build/test/$(TEST_BIN)
	rm -rf benchmark/test/$(TEST_BIN).cc
	rm -rf benchmark/test/$(TEST_BIN).dep

distclean:
	rm -rf benchmark
